﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using AgileTimeTracker.Domain.Common;
using AgileTimeTracker.Domain.TfsModule;
using AgileTimeTracker.Domain.UserModule;
using Cwi.Utils;
using ZetaTfsApi;

namespace AgileTimeTracker.Infra.Repository.Tfs
{
    public class TfsRepository : ITfsRepository
    {
        private UserProject project;

        public void SetProject(UserProject project)
        {
            this.project = project;
        }

        public TfsRepository()
        {
        }

        public void Test()
        {
            using (var server = this.CreateServerController())
            {
                var projectController = this.CreateProjectController(server);
                var workItem = projectController.GetWorkItemsByIDs(2068).FirstOrDefault();
            }
        }

        public BaseWorkItem GetWorkItem(int id)
        {
            using (var server = this.CreateServerController())
            {
                var projectController = this.CreateProjectController(server);
                var tfsWorkItem = projectController.GetWorkItemsByIDs(id).FirstOrDefault();
                if (tfsWorkItem == null)
                {
                    return null;
                }
                               
                var workItem = new BaseWorkItem();                
                this.Translate(tfsWorkItem, ref workItem, projectController);
                workItem.Id = id;
                return workItem;
            }
        }

        public void SaveWorkItem(BaseWorkItem workItem)
        {
            using (var server = this.CreateServerController())
            {
                var projectController = this.CreateProjectController(server);
                var tfsWorkItem = projectController.GetWorkItemsByIDs(workItem.Id).FirstOrDefault();
                tfsWorkItem.Fields.FirstOrDefault(t => t.Name == "Completed Work").Value = workItem.CompletedWork;
                tfsWorkItem.Fields.FirstOrDefault(t => t.Name == "Remaining Work").Value = workItem.RemainingWork;
                tfsWorkItem.Save();
            }
        }

        private string GetFields(TfsWorkItem workItem)
        {
            var builder = new StringBuilder();
            foreach (var field in workItem.Fields)
            {
                builder.AppendFormat("{0}: {1}\n", field.Name, field.Value);
            }

            return builder.ToString();
        }

        private void Translate<T>(TfsWorkItem source, ref T target, ProjectController projectController) where T : BaseTfsItem
        {
            var properties = target.GetType().GetProperties();

            foreach (var prop in properties)
            {
                this.GetWorkItemField(source, ref target, prop);
            }

            if (target is BaseWorkItem && source.Links.Count > 0)
            {
                var workItem = target as BaseWorkItem;
                var link = source.Links.Where(t => t.BaseType == TfsBaseType.RelatedLink).FirstOrDefault();
                if (link == null)
                {
                    return;
                }

                var tfsLink = link.GetType().GetField("_link", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance).GetValue(link) as Microsoft.TeamFoundation.WorkItemTracking.Client.RelatedLink;
                if (tfsLink != null)
                {
                    var relatedWorkItem = projectController.GetWorkItemsByIDs(tfsLink.RelatedWorkItemId).FirstOrDefault();
                    if (relatedWorkItem != null)
                    {
                        workItem.UserStory = relatedWorkItem.Title;
                    }
                }
            }
        }


        private void GetWorkItemField<T>(TfsWorkItem source, ref T target, PropertyInfo propInfo) where T : BaseTfsItem
        {
            var fieldNameWords = propInfo.Name.PascalToWord();
            var field = source.Fields.FirstOrDefault(t => t.Name == fieldNameWords);
            if (field == null || field.Value == null)
            {
                return;
            }

            var fieldVal = field.Value;

            var typedVal = Convert.ChangeType(fieldVal, propInfo.PropertyType);

            propInfo.SetValue(target, typedVal);
        }

        private ServerController CreateServerController()
        {
            Check.NotNull(this.project, "project");

            return new ServerController(new ServerConfiguration
            {
                DomainName = project.Domain,
                Password = project.Password,
                ServerUrl = project.Url,
                UseImpersonation = true,
                UserName = project.Login
            });
        }

        private ProjectController CreateProjectController(ServerController serverController)
        {
            return new ProjectController(serverController.ServerConfiguration, serverController.GetProject(this.project.Name));
        }
    }
}
